package netective.gui;

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import netective.api.analyzer.PacketType;
import netective.api.analyzer.PacketType.PacketResult;
import netective.api.analyzer.PredictionType;
import netective.api.gatherer.DeviceInterface;
import netective.engine.Engine;
import netective.gui.charts.ChartGrain;
import netective.gui.charts.ChartType;
import netective.gui.charts.TimeChartFactory;

@SuppressWarnings("serial")
public class MainWindow extends JFrame implements ActionListener {
	// constants
	private static final int WINDOW_WIDTH = 1200;
	private static final int WINDOW_HEIGHT = 600;
	private static final String APP_TITLE = "NETECTIVE";
	
	// other gui elements to which 'this' has access
	SettingsDevicesWindow settingsDevicesWindow = null;
	
	JTabbedPane tabsCharts;
	
	ArrayList<TimeSeriesChartPanel> timeSeriesChartPanels = null;
	
	// elements in this window
	JMenuBar menuBar;
	JMenu fileMenu;
	JMenu settingsMenu;
	JMenuItem exitMenuItem;
	JMenuItem devicesMenuItem;
	PopupMenu popup;
	MenuItem minimize;
	
	private boolean isUp;
	
	public MainWindow() {
		this.setSize(new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT));
		this.setTitle(APP_TITLE);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		isUp=true;
		setMenuBar();
		
		PacketType[] packetType = PacketType.getAllPacketTypes();
		ArrayList<PacketType> packetTypesArr = new ArrayList<PacketType>();
		for (int i = 0; i < packetType.length; i++) {
			packetTypesArr.add(packetType[i]);
		}
		
		ChangeListener changeListener = new ChangeListener() {
			public void stateChanged(ChangeEvent changeEvent) {
				refreshCharts();
			}
		};
		tabsCharts = new JTabbedPane();
		tabsCharts.addChangeListener(changeListener);
		this.add(tabsCharts);
		
		this.setVisible(true);
		addCharts();

		trayIcon();
		
	}
	
	private void addCharts() 
	{
		int selectedTab = tabsCharts.getSelectedIndex() >= 0 ? tabsCharts.getSelectedIndex() : 0;
		
		ArrayList<ArrayList<PacketType>> selectedLists = new ArrayList<ArrayList<PacketType>>();
		ArrayList<ChartGrain> selectedGrains = new ArrayList<ChartGrain>();
		ArrayList<PredictionType> selectedPredictions = new ArrayList<PredictionType>();
		if (timeSeriesChartPanels!=null) {
			for (TimeSeriesChartPanel t:timeSeriesChartPanels)
			{
				selectedLists.add(t.getSelectedPackets());
				selectedGrains.add(t.getSelectedGrain());
				selectedPredictions.add(t.getSelectedPrediction());
				tabsCharts.remove(t);
			}
		}
		if (selectedLists.size() == 0) {
			selectedLists.add(PacketType.getPacketTypesAsArrayList(PacketResult.ALL));
			selectedLists.add(PacketType.getPacketTypesAsArrayList(PacketResult.WITHOUT_ARP));
			selectedLists.add(PacketType.getPacketTypesAsArrayList(PacketResult.WITHOUT_ARP));
			selectedLists.add(PacketType.getPacketTypesAsArrayList(PacketResult.WITHOUT_ARP));
			selectedLists.add(PacketType.getPacketTypesAsArrayList(PacketResult.TCP_AND_UDP));
			selectedLists.add(PacketType.getPacketTypesAsArrayList(PacketResult.TCP_AND_UDP));
			selectedLists.add(PacketType.getPacketTypesAsArrayList(PacketResult.TCP_AND_UDP));
			selectedLists.add(PacketType.getPacketTypesAsArrayList(PacketResult.WITHOUT_ARP));
		}
		if (selectedGrains.size() == 0) {
			for (int i = 0; i < selectedLists.size(); i++) {
				selectedGrains.add(ChartGrain.Minute);
			}
		}
		if (selectedPredictions.size() == 0) {
			for (int i = 0; i < selectedLists.size(); i++) {
				selectedPredictions.add(PredictionType.None);
			}
		}
		
		timeSeriesChartPanels=new ArrayList<TimeSeriesChartPanel>();
		
		timeSeriesChartPanels.add(new TimeSeriesChartPanel(TimeChartFactory.classesOfPackets(selectedGrains.get(0), selectedPredictions.get(0),  
				selectedLists.get(0)), ChartType.ClassesOfPackets, PacketType.getAllPacketTypes(), 
				selectedLists.get(0), selectedGrains.get(0), selectedPredictions.get(0)));
		
		timeSeriesChartPanels.add(new TimeSeriesChartPanel(TimeChartFactory.dirrectionOfPackets(selectedGrains.get(1), selectedPredictions.get(1),  
				TimeSeriesChartPanel.getSelectedPacketsToString(selectedLists.get(1))),
				ChartType.DirectionOfPackets, PacketType.getPacketTypesWithoutARP(), 
				selectedLists.get(1), selectedGrains.get(1), selectedPredictions.get(1)));
		
		timeSeriesChartPanels.add(new TimeSeriesChartPanel(TimeChartFactory.downloadUpload(selectedGrains.get(2), selectedPredictions.get(2), TimeSeriesChartPanel.getSelectedPacketsToString(selectedLists.get(2))), 
				ChartType.DownloadUpload, PacketType.getPacketTypesWithoutARP(), 
				selectedLists.get(2), selectedGrains.get(2), selectedPredictions.get(2)));
		
		timeSeriesChartPanels.add(new TimeSeriesChartPanel(TimeChartFactory.downloadUploadAverage(selectedGrains.get(3), selectedPredictions.get(3), TimeSeriesChartPanel.getSelectedPacketsToString(selectedLists.get(3))), 
				ChartType.DownloadUploadAverage, PacketType.getPacketTypesWithoutARP(), 
				selectedLists.get(3), selectedGrains.get(3), selectedPredictions.get(3)));
		
		timeSeriesChartPanels.add(new TimeSeriesChartPanel(TimeChartFactory.outPort(selectedGrains.get(4), selectedPredictions.get(4), 10, TimeSeriesChartPanel.getSelectedPacketsToString(selectedLists.get(4))), 
				ChartType.OutPort, PacketType.getTCPAndUDPPackets(), 
				selectedLists.get(4), selectedGrains.get(4), selectedPredictions.get(4)));
		
		timeSeriesChartPanels.add(new TimeSeriesChartPanel(TimeChartFactory.inPort(selectedGrains.get(5), selectedPredictions.get(5),  10, TimeSeriesChartPanel.getSelectedPacketsToString(selectedLists.get(5))), 
				ChartType.InPort, PacketType.getTCPAndUDPPackets(), 
				selectedLists.get(5), selectedGrains.get(5), selectedPredictions.get(5)));
		
		timeSeriesChartPanels.add(new TimeSeriesChartPanel(TimeChartFactory.otherPort(selectedGrains.get(6), selectedPredictions.get(6), 10, TimeSeriesChartPanel.getSelectedPacketsToString(selectedLists.get(6))), 
				ChartType.OtherPort, PacketType.getTCPAndUDPPackets(), 
				selectedLists.get(6), selectedGrains.get(6), selectedPredictions.get(6)));

		timeSeriesChartPanels.add(new TimeSeriesChartPanel(TimeChartFactory.otherAdresses(selectedGrains.get(7), selectedPredictions.get(7), 10, TimeSeriesChartPanel.getSelectedPacketsToString(selectedLists.get(7))), 
				ChartType.OtherAdresses, PacketType.getTCPAndUDPPackets(), 
				selectedLists.get(7), selectedGrains.get(7), selectedPredictions.get(7)));		
		
		for (TimeSeriesChartPanel t:timeSeriesChartPanels)
		{
			tabsCharts.addTab(t.getChart().getChart().getTitle().getText(), t);
		}
		
		tabsCharts.setSelectedIndex(selectedTab);
	
		timeSeriesChartPanels.get(this.tabsCharts.getSelectedIndex()).repaint();
		this.setVisible(true);
		System.out.println("Repaint chart");
	}

	public void setMenuBar() {
		menuBar = new JMenuBar();
		
		fileMenu = new JMenu("File");
		settingsMenu = new JMenu("Settings");
		
		exitMenuItem = new JMenuItem("Exit");
		exitMenuItem.addActionListener(this);
		
		devicesMenuItem = new JMenuItem("Devices");
		devicesMenuItem.addActionListener(this);
		
		fileMenu.add(exitMenuItem);
		
		settingsMenu.add(devicesMenuItem);
		
		menuBar.add(fileMenu);
		menuBar.add(settingsMenu);
		
		this.setJMenuBar(menuBar);
	}
	
	private void exitApp() {
		this.dispose();
		System.exit(0);
	}
	
	private void showDevicesSettingsWindow() {
		createInstanceSettingsDevicesWindow(null);
		settingsDevicesWindow.setVisible(true);
	}
	
	public void createInstanceSettingsDevicesWindow(Engine e) {
		if (settingsDevicesWindow == null) {
			settingsDevicesWindow = new SettingsDevicesWindow(e);
			settingsDevicesWindow.setSize(600, 300);
		}
		settingsDevicesWindow.setVisible(false);
	}
	
	public void setDevices(DeviceInterface[] devices) {
		createInstanceSettingsDevicesWindow(null);
		settingsDevicesWindow.setDevices(devices);
	}
	
	public void startChartRefreshing()
	{
		while(true)
		{
			try 
			{
				Thread.sleep(10 * 1000); //repaint chart every 10s
			} 
			catch (InterruptedException e) 
			{
				e.printStackTrace();
			}
			if (isUp)
			{
				repaintView();
			}
		}
	}
	
	public void repaintView()
	{
		//addCharts();
		refreshCharts();
		System.out.println("Repaint chart");		
	}
	
	public void refreshCharts() {
		int selectedTab = tabsCharts.getSelectedIndex();
		
		switch (selectedTab) {
		case 0:
		timeSeriesChartPanels.get(0).setChartAndRefresh(TimeChartFactory.classesOfPackets(
				timeSeriesChartPanels.get(0).getSelectedGrain(), 
				timeSeriesChartPanels.get(0).getSelectedPrediction(), 
				timeSeriesChartPanels.get(0).getSelectedPackets()).getChart());
		break;
		
		case 1:
		timeSeriesChartPanels.get(1).setChartAndRefresh(TimeChartFactory.dirrectionOfPackets(
				timeSeriesChartPanels.get(1).getSelectedGrain(), 
				timeSeriesChartPanels.get(1).getSelectedPrediction(), 
				TimeSeriesChartPanel.getSelectedPacketsToString(
						timeSeriesChartPanels.get(1).getSelectedPackets())).getChart());
		break;
		
		case 2:
		timeSeriesChartPanels.get(2).setChartAndRefresh(TimeChartFactory.downloadUpload(
				timeSeriesChartPanels.get(2).getSelectedGrain(), 
				timeSeriesChartPanels.get(2).getSelectedPrediction(), 
				TimeSeriesChartPanel.getSelectedPacketsToString(
						timeSeriesChartPanels.get(2).getSelectedPackets())).getChart());
		break;
		
		case 3:
		timeSeriesChartPanels.get(3).setChartAndRefresh(TimeChartFactory.downloadUploadAverage(
				timeSeriesChartPanels.get(3).getSelectedGrain(), 
				timeSeriesChartPanels.get(3).getSelectedPrediction(), 
				TimeSeriesChartPanel.getSelectedPacketsToString(
						timeSeriesChartPanels.get(3).getSelectedPackets())).getChart());
		break;
		
		case 4:
		timeSeriesChartPanels.get(4).setChartAndRefresh(TimeChartFactory.outPort(
				timeSeriesChartPanels.get(4).getSelectedGrain(), 
				timeSeriesChartPanels.get(4).getSelectedPrediction(), 10, 
				TimeSeriesChartPanel.getSelectedPacketsToString(
						timeSeriesChartPanels.get(4).getSelectedPackets())).getChart());
		break;
		
		case 5:
		timeSeriesChartPanels.get(5).setChartAndRefresh(TimeChartFactory.inPort(
				timeSeriesChartPanels.get(5).getSelectedGrain(), 
				timeSeriesChartPanels.get(5).getSelectedPrediction(), 10,
				TimeSeriesChartPanel.getSelectedPacketsToString(
						timeSeriesChartPanels.get(5).getSelectedPackets())).getChart());
		break;
		
		case 6:
		timeSeriesChartPanels.get(6).setChartAndRefresh(TimeChartFactory.otherPort(
				timeSeriesChartPanels.get(6).getSelectedGrain(), 
				timeSeriesChartPanels.get(6).getSelectedPrediction(), 10,
				TimeSeriesChartPanel.getSelectedPacketsToString(
						timeSeriesChartPanels.get(6).getSelectedPackets())).getChart());
		break;
		
		case 7:
		timeSeriesChartPanels.get(7).setChartAndRefresh(TimeChartFactory.otherAdresses(
				timeSeriesChartPanels.get(7).getSelectedGrain(), 
				timeSeriesChartPanels.get(7).getSelectedPrediction(), 10,
				TimeSeriesChartPanel.getSelectedPacketsToString(
						timeSeriesChartPanels.get(7).getSelectedPackets())).getChart());
		break;
		}
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		Object source = e.getSource();
		if (source.equals(exitMenuItem)) {
			exitApp();
		}
		else if (source.equals(devicesMenuItem)) {
			showDevicesSettingsWindow();
		}
	}
	
	
	public void trayIcon()
	{
        if (!SystemTray.isSupported()) {
            System.out.println("SystemTray is not supported");
            return;
        }
		try {
			BufferedImage img = ImageIO.read(getClass().getResource("/netective/logo.gif"));		
			
            popup = new PopupMenu();
            minimize = new MenuItem("Minimize to tray");  
	         minimize.addActionListener(new ActionListener() {  
	             public void actionPerformed(ActionEvent arg0) {  
	            	 changeVisibility();  
	             }  
	         });
	         MenuItem exit = new MenuItem("Exit");  
	      exit.addActionListener(new ActionListener() {  
	          public void actionPerformed(ActionEvent arg0) {  
	              System.exit(0);  
	          }  
	      });
	      popup.add(minimize);  
	      popup.add(exit);  
            
          final TrayIcon trayIcon = new TrayIcon(img,"Netective",popup);
      	  final SystemTray tray = SystemTray.getSystemTray();  
      	
          trayIcon.addMouseListener(new MouseListener() {

				@Override
				public void mouseClicked(MouseEvent e) {
                  if (e.getButton() == 1)  
                  	changeVisibility(); 
					
				}

				@Override
				public void mousePressed(MouseEvent e) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void mouseReleased(MouseEvent e) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void mouseEntered(MouseEvent e) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void mouseExited(MouseEvent e) {
					// TODO Auto-generated method stub
					
				}  
    
          }); 
        	
        	tray.add(trayIcon);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (AWTException e) {
			e.printStackTrace();
		}
    	
        
        
	}
	
    private void changeVisibility() {  
    	
        if (this.isVisible() == true) {
        	isUp=false;
        	if(settingsDevicesWindow.isVisible()==true)
        	{
        		settingsDevicesWindow.dispose();
        	}
        	minimize.setLabel("Open");  
            this.setVisible(false); 
            
        } else {  
            this.setVisible(true); 
            repaintView();
            minimize.setLabel("Minimize to tray");
            isUp=true;
        }  
    } 
	
}
